#pragma once

#include "funpl.h"
#include "ast.h"
#include <vector>

namespace funpl
{

namespace ast
{

/// Declaration node.
struct declaration : public node
{
protected:
	/// Constructor.
	declaration(node_type::t type, node *parent = nullptr)
		: node(type, parent) { }
};

struct type;

/// Typegroup node.
struct typegroup : public declaration
{
	util::const_char_range name;

	typedef std::vector<type*> element_vector;
	element_vector elements;

	/// Constructor.
	typegroup(node *parent = nullptr,
		const util::const_char_range &name = util::empty_const_char_range())
			: declaration(node_type::typegroup, parent),
			name(name) { }
};

struct metadef;

/// Template parameter
struct metaparameters : public node
{
	typedef std::pair<util::const_char_range, metadef*> parameter;
	typedef std::vector<parameter>  parameter_vector;
	parameter_vector parameters;

	/// Constructor.
	metaparameters(node *parent = nullptr)
			: node(node_type::metaparameters, parent) { }
};

struct metaarguments;

/// Structure node.
struct struct_ : public declaration
{
	util::const_char_range name;

	metaparameters *metaparameters;
	metaarguments *specialization;

	typedef std::pair<util::const_char_range, node*> element;
	typedef std::vector<element> element_vector;
	element_vector elements;

	/// Constructor.
	struct_(node *parent = nullptr,
		const util::const_char_range &name = util::empty_const_char_range())
			: declaration(node_type::struct_, parent),
			name(name),
			metaparameters(),
			specialization() { }
};

struct metaarguments;

/// Typedef node.
struct typedef_ : public declaration
{
	util::const_char_range name;

	type *alias;

	metaparameters *metaparameters;
	metaarguments *specialization;

	/// Constructor.
	typedef_(node *parent = nullptr,
		const util::const_char_range &name = util::empty_const_char_range())
			: declaration(node_type::typedef_, parent),
			name(name),
			alias(),
			metaparameters(),
			specialization() { }
};

struct metaarguments;

/// Class node.
struct class_ : public declaration
{
	util::const_char_range name;

	metaparameters *metaparameters;
	metaarguments *specialization;

	typedef std::pair<util::const_char_range, node*> element;
	typedef std::vector<element> element_vector;
	element_vector elements;

	/// Constructor.
	class_(node *parent = nullptr,
		const util::const_char_range &name = util::empty_const_char_range())
			: declaration(node_type::class_, parent),
			name(name),
			metaparameters(),
			specialization() { }
};

/// Function class.
namespace function_class
{
	/// Enumeration.
	enum t
	{
		normal,
		reference,
		constructor,
		destructor
	};
}

struct variable;

/// Function node.
struct function_declaration : public declaration
{
	function_class::t functionClass;
	bool stackOnHeap;

	util::const_char_range name;

	metaparameters *metaparameters;

	typedef std::pair<util::const_char_range, variable*> parameter; // TODO: factor out?!?
	typedef std::vector<parameter> parameter_vector;
	parameter_vector parameters;

	type *returnType;

	/// Constructor.
	function_declaration(node *parent = nullptr,
		const util::const_char_range &name = util::empty_const_char_range())
			: declaration(node_type::function, parent),
			functionClass(function_class::normal),
			stackOnHeap(false),
			name(name),
			returnType() { }
};

/// Function node.
struct function : public declaration
{
	function_declaration *decl;

	expression *body;
	bool autoReturn;

	/// Constructor.
	function(node *parent = nullptr,
		const util::const_char_range &name = util::empty_const_char_range())
			: declaration(node_type::function, parent),
			decl(),
			body(),
			autoReturn(false) { }
};

} // namespace

} // namespace